﻿using Dot5Demo.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Resources;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Globalization;
using System.Threading;

namespace Dot5Demo
{
    /*
    * 动态生成资源
    https://docs.microsoft.com/en-us/dotnet/api/system.resources.resourcewriter?redirectedfrom=MSDN&view=net-6.0
    */
    public static class ResourceExtensions
    {

        public static event EventHandler CultureChanged;

        public static void SetCulture(string langName)
        {
            if (string.IsNullOrEmpty(langName))
                return;
            try
            {
                var info = CultureInfo.GetCultureInfo(langName);
                SetCulture(info);
            }
            catch (Exception ex)
            {
                MessageBox.Show("SetCulture \n" + ex.Message, "Error");
            }
        }

        public static CultureInfo CultureInfo { get; private set; }
        public static void SetCulture(CultureInfo info)
        {
            if (info == null)
                return;
            if (!CultureInfo.CurrentCulture.Equals(info))
            {
#if NET5_0_OR_GREATER
                CultureInfo.CurrentCulture = info;
                CultureInfo.CurrentUICulture = info;
#endif //NET5_0_OR_GREATER
                Thread.CurrentThread.CurrentCulture = info;
                Thread.CurrentThread.CurrentUICulture = info;
                resourceMan = null;
                CultureInfo = info;
                CultureChanged?.Invoke(null, null);
            }
        }

        private static ResourceManager resourceMan;

        internal static ResourceManager ResourceManager
        {
            get
            {
                if (ReferenceEquals(resourceMan, null))
                {
                    ResourceManager temp = ResourceManager.CreateFileBasedResourceManager("Dot5Demo", TempResPath, null);
                    resourceMan = temp;
                }
                return resourceMan;
            }
            set
            {
                resourceMan = value;
            }
        }

        private static string TempResDir = ".temp";
        private static string TempResPath;
        private static string ResDir = "locales";
        private static string ResDirPath;
        private static string ResConfigFile = "resouces.xml";

        private static ResConfig Config { get; set; }

        public static void InitResouceInfo()
        {
            var f = Path.Combine(TempResPath, ResConfigFile);
            if (File.Exists(f))
            {
                ResConfig conf;
                if (SerializationUtil.XmlDeserializeFromFile(f, out conf))
                {
                    Config = conf;
                    if (ChecksumResFile())
                        return;
                }
            }
            GenerateConfig();
            SerializationUtil.XmlSerializeToFile(Config, f);
        }

        public static void GenerateConfig()
        {
            var conf = new ResConfig()
            {
                Items = new List<ResItem>()
                    {
                        new ResItem(){ LangName="zh-CN"},
                        new ResItem(){ LangName="en-US"},
                    },
            };
            var files = new DirectoryInfo(ResDirPath).GetFiles();
            foreach (var item in conf.Items)
            {
                var pat = string.Format(@"^\S+\.{0}\.txt$", item.LangName);
                foreach (var f in files)
                {
                    if (Regex.IsMatch(f.FullName, pat, RegexOptions.IgnoreCase))
                    {
                        item.Files.Add(new ResFileInfo(f.Name, FileCheckUtil.GetFileChecksum(f.FullName)));
                    }
                }
            }
            foreach (var item in conf.Items)
            {
                var file = string.Format("{0}.{1}.resources", "Dot5Demo", item.LangName);
                file = Path.Combine(TempResPath, file);
                if (File.Exists(file))
                    File.Delete(file);
                ResourceWriter resx = new ResourceWriter(file);
                foreach (var f in item.Files)
                {
                    AddResFile(ref resx, Path.Combine(ResDirPath, f.File));
                }
                resx.Generate();
                resx.Close();
                resx.Dispose();
                item.ResFileName = Path.GetFileName(file);
                item.ResKey = FileCheckUtil.GetFileChecksum(file);
            }
            Config = conf;
        }

        public static void AddResFile(ref ResourceWriter resx, string file)
        {
            using (var reader = new StreamReader(File.OpenRead(file)))
            {
                while (!reader.EndOfStream)
                {
                    var line = reader.ReadLine().Trim();
                    if (string.IsNullOrEmpty(line))
                        continue;
                    if (line.StartsWith(";") || line.StartsWith("#"))
                    {
                        continue;
                    }

#if NET5_0_OR_GREATER
                    var res = line.Split('=', 2);
                    if (res.Length >= 2)
                    {
                        resx.AddResource(res[0], res[1]);
                    }
#endif //NET5_0_OR_GREATER
                }
            }
        }

        public static bool ChecksumResFile()
        {
            if (Config == null)
                return false;
            foreach (var item in Config.Items)
            {
                if (item == null)
                    continue;
                if (item.Files != null)
                {
                    foreach (var f in item.Files)
                    {
                        var df = Path.Combine(ResDirPath, f.File);
                        if (!File.Exists(df))
                            return false;
                        var v = FileCheckUtil.GetFileChecksum(df);
                        if (v != f.FileKey)
                            return false;
                    }
                }
                var r = Path.Combine(TempResPath, item.ResFileName);
                if (!File.Exists(r))
                    return false;
                var rv = FileCheckUtil.GetFileChecksum(r);
                if (rv != item.ResKey)
                    return false;
            }
            return true;
        }

        static ResourceExtensions()
        {
            //ResourceWriter
            //var tpath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            TempResPath = Path.Combine(Application.StartupPath, TempResDir);
            if (!Directory.Exists(TempResPath))
            {
                var di = Directory.CreateDirectory(TempResPath);
                di.Attributes = FileAttributes.Directory | FileAttributes.Hidden;
            }
            ResDirPath = Path.Combine(Application.StartupPath, ResDir);
            CultureInfo = CultureInfo.CurrentCulture;
        }

        public static string CultureString(this string str)
        {
            //if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
            //{
            //    return str;
            //}
            var ret = Properties.Resources.ResourceManager.GetString(str);
            if (!string.IsNullOrEmpty(ret))
            {
                return ret;
            }
            try
            {
                ret = ResourceManager.GetString(str,CultureInfo);
                if (!string.IsNullOrEmpty(ret))
                {
                    return ret;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(str + "\n" + ex.Message);
            }
            return str;
        }
    }

    public class ResConfig
    {
        public ResConfig()
        {
            Items = new List<ResItem>();
        }

        public List<ResItem> Items { get; set; }
    }

    public class ResItem
    {
        public ResItem()
        {
            Files = new List<ResFileInfo>();
        }

        public string LangName { get; set; }
        public List<ResFileInfo> Files { get; set; }
        public string ResFileName { get; set; }
        public string ResKey { get; set; }
    }

    public class ResFileInfo
    {
        public ResFileInfo()
        {
        }

        public ResFileInfo(string file, string fileKey)
        {
            File = file;
            FileKey = fileKey;
        }

        public string File { get; set; }
        public string FileKey { get; set; }
    }
}